package com.moli.iov.alarm.remote.impl;

import com.moli.iov.alarm.model.po.AlarmInfo;
import com.moli.iov.alarm.model.po.StatAlarm;
import com.moli.iov.alarm.remote.IAlarmQuantityStatRemoteService;
import com.moli.iov.alarm.service.stat.AlarmStatService;
import com.moli.iov.base.model.po.VehicleBase;
import com.moli.iov.base.remote.IVehicleBaseRemoteService;
import com.moli.iov.handle.alarm.enums.AlarmCodeEnum;
import com.moli.iov.handle.alarm.enums.AlarmLevelEnum;
import com.moli.iov.rest.RestResponse;
import com.moli.iov.sys.model.po.Company;
import com.moli.iov.sys.remote.ICompanyRemoteService;
import com.moli.iov.util.ConstactUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.moli.iov.alarm.model.po.StatAlarm.*;
import static java.util.stream.Collectors.*;

/**
 *
 * Alarm quantity statistics
 *
 * @author OAK
 * @version 3.0
 * @since 2019/10/21 10:44 AM.
 *
 */
@Slf4j
@Service(group = "${service.group}")
public class AlarmQuantityStatRemoteServiceImpl implements IAlarmQuantityStatRemoteService {

    @Resource
    AlarmStatService alarmStatService;

    @Reference(group = "${service.group}", check = false)
    ICompanyRemoteService companyRemoteService;

    @Reference(group = "${service.group}", check = false)
    IVehicleBaseRemoteService vehicleBaseRemoteService;

    private AtomicInteger executeFinishCount = new AtomicInteger();

    private Map<Integer, String> alarmAttrStatMap = new ConcurrentHashMap<Integer, String>(){{
        put(AlarmCodeEnum.OUT_FENCE.getFlag(), OUT_FENCE_ALARM_COUNT);
        put(AlarmCodeEnum.IN_FENCE.getFlag(), IN_FENCE_ALARM_COUNT);
        put(AlarmCodeEnum.OFF_LINE.getFlag(), OFF_LINE_COUNT);
        put(AlarmCodeEnum.MILEAGE_EXCEPTION_CHANGE.getFlag(), MILEAGE_EXCEPTION_CHANGE_COUNT);
        put(AlarmLevelEnum.ONE.getFlag(), ONE_LEVEL_ALARM_COUNT);
        put(AlarmLevelEnum.PLATFORM.getFlag(), TWO_LEVEL_ALARM_COUNT);
        put(AlarmLevelEnum.THREE.getFlag(), THREE_LEVEL_ALARM_COUNT);
        put(-1, DEFAULT_VALUE);
    }};

    private Integer fetchGroupKey(AlarmInfo alarmInfo){
        List<Integer> terminalAlarms = AlarmCodeEnum.getTerminalAlarms();
        if(AlarmCodeEnum.IN_FENCE.getFlag() == alarmInfo.getAlarmCode()
                || AlarmCodeEnum.OUT_FENCE.getFlag() == alarmInfo.getAlarmCode()
                || AlarmCodeEnum.OFF_LINE.getFlag() == alarmInfo.getAlarmCode()
            || AlarmCodeEnum.MILEAGE_EXCEPTION_CHANGE.getFlag() == alarmInfo.getAlarmCode()){
            return alarmInfo.getAlarmCode();
        } else {
            if(terminalAlarms.contains(alarmInfo.getAlarmCode())){
                return alarmInfo.getAlarmLevel();
            }else{
                return -1;
            }
        }
    }

    @Override
    public RestResponse<String> executeGenericAlarmQuantityStat(String currentDate) {
        log.info("Start synchronized execute generic alarm quantity statistics.");
        RestResponse<List<Company>> restResponse = companyRemoteService.listCompany(null);
        if(RestResponse.isSuccess(restResponse)){
            List<Company> companyList = restResponse.getData();
            if(!companyList.isEmpty()){
                List<AlarmInfo> alarmList = alarmStatService.resolveAlarmInfo(currentDate);
                Map<Long, List<AlarmInfo>> alarmForCompanySubList = alarmList.stream().collect(groupingBy(AlarmInfo::getCompanyId));
                StatAlarm totalStatAlarm = new StatAlarm();
                totalStatAlarm.setCompanyId(ConstactUtil.TOTAL_COMPANY_ID);
                totalStatAlarm.setDate(currentDate);
                companyList.stream().forEach(company -> {
                    Long companyId = company.getCompanyId();
                    if (!alarmForCompanySubList.containsKey(companyId)) {
                        return;
                    }
                    List<AlarmInfo> alarmSubList = alarmForCompanySubList.get(companyId);
                    StatAlarm statAlarm = new StatAlarm();
                    statAlarm.setCompanyId(companyId);
                    statAlarm.setDate(currentDate);
                    List<VehicleBase> vehicleBaseDtoList = vehicleBaseRemoteService.listVehicleBaseByCompanyId(companyId);
                    if(vehicleBaseDtoList != null){
                        statAlarm.setVehicleCount(new Long(vehicleBaseDtoList.size()));
                    }
                    Map<String, Long>  alarmStatMap = alarmSubList.stream().collect(groupingBy(alarmInfo ->
                                    alarmAttrStatMap.get(fetchGroupKey(alarmInfo)),
                            Collectors.summingLong(value -> 1)
                    ));
                    Class statAlarmClass = statAlarm.getClass();
                    alarmStatMap.entrySet().stream().filter(e -> !e.getKey().equals(DEFAULT_VALUE)).forEach(e -> {
                        try {
                            Field field = statAlarmClass.getDeclaredField(e.getKey());
                            field.setAccessible(true);
                            field.set(statAlarm, e.getValue());
                        } catch (NoSuchFieldException e1) {
                            log.error("StatAlarm Field no such field, {}.", e1);
                        }catch (IllegalAccessException e1) {
                            log.error("StatAlarm Field set illegal access find a fail, {}.", e1);
                        }
                    });
                    totalStatAlarm.add(statAlarm);
                    executeFinishCount.addAndGet(1);
                    if( saveStatAlarm(statAlarm) == null ){
                        executeFinishCount.decrementAndGet();
                    }
                });
                executeFinishCount.addAndGet(1);
                if( saveStatAlarm(totalStatAlarm) == null ){
                    executeFinishCount.decrementAndGet();
                }
            }
        }
        log.info("Finish synchronized execute generic alarm quantity statistics, Perform to success for {}.", executeFinishCount.get());
        return RestResponse.success("生成报警统计成功");
    }

    @Override
    public StatAlarm saveStatAlarm(StatAlarm statAlarm) {
        return alarmStatService.saveStatAlarm(statAlarm);
    }

}
